package br.usp.wta.inductor;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import br.usp.language.automata.State;
import br.usp.language.automata.StateMachine;
import br.usp.language.automata.Transition;


//TODO: mas...e o estado FINALLLL, hein, JJ?
public class JJ98 {
	StateMachine Ap;
	StateMachine As;

	
    public JJ98() {
    }

    /**
     * Faz o merge dos automatos de prefixo e sufixo.
     * @param Ap
     * @param As
     * @return
     */
    public StateMachine geraAc(StateMachine Ap, StateMachine As) {
    	this.Ap=Ap;
    	this.As=As;
    	step1();
    	
        return Ac;
    }
    
    private int i,j;
    private int x,y;
    private StateMachine Ac;
    private String sigma, rho;
    private Map<Integer, Set<State>> mapAp = new HashMap<Integer,Set<State>>();
    private Map<Integer, Set<State>> mapAs = new HashMap<Integer,Set<State>>();
    private Map<Integer, Set<State>> mapAc = new HashMap<Integer,Set<State>>();
    
    private void addToClassNumber(Map<Integer,Set<State>> m, State s, Integer n){
    	if(m.containsKey(n))    		
    		(m.get(n)).add(s);
    	else{
	    	Set<State> newSet = new HashSet<State>();
	    	newSet.add(s);
	    	m.put(n, newSet);
    	}
    }
    
    private Integer stateClassNumber(Map<Integer,Set<State>> m, State s){
    	for(Integer k: m.keySet()){
    		if(m.get(k).contains(s))
    			return k;
    	}
    	return null;
    }
    
    
     //TODO: e a propagacao inicial??
    private void step1(){    	
    	i=0;j=0;//TODO: i=1?
    	Ac = new StateMachine("Ac");
    	
    	Set<State> setAp = new HashSet<State>();
    	setAp.add((Ap.getInitialState()));
    	mapAp.put(i, setAp);
    	
    	Set<State> setAs = new HashSet<State>();
    	setAs.add((As.getInitialState()));
    	mapAs.put(j, setAs);//root of As = j
    	
    	Set<State> setAc = new HashSet<State>();
    	setAc.add((Ac.getInitialState()));
    	mapAc.put(i, setAc);//TODO: i= 1 ou 0?
    	
    	//propagacao inicial do 'estado 0' no AS, segundo DESENHO da IVONE
    	for(Transition t: As.getInitialState().getAllTransitions())
    		if(t.isEpsilon())
    			addToClassNumber(mapAs,t.getStateTo(),j);
    	
    	step2(Ap.getInitialState());
    	stepFinal();
    }
    
    
    // cria estados de aceitação de acordo com classe de aceitação em As
    private void stepFinal() {

        // acha classe de aceitação em As
        Integer classe = null;
        for (State s: As.getAllStates()) {
            if (s.isAcceptState()) {
                classe = stateClassNumber(mapAs, s);
                break;
            }
        }
        
        // procura estado correspondente em Ac
        if (classe == null) {
            System.out.println("Deu coisa errada");
            return;
        }
        for (State s: Ac.getAllStates()) {
            if (stateClassNumber(mapAc, s).equals(classe))
                s.setAcceptState();
        }
    }

    private void step2(State po){
    	
    	
        if(po.getAllTransitions().isEmpty()){
            step17();
        }
        else{
	        for(Transition t: po.getAllTransitions()){
	        	State pd = t.getStateTo();
	        	sigma = t.getConditions().get(0);
	        	
	        	//pd has a number
	        	if(stateClassNumber(mapAp,pd) != null){
	        		//pd jah tem numero
	        	}else{
	        		i++;
	        		//addToClassNumber(mapAp,pd,i);//pd = i; TODO: linha faltante na IVONE?
	        		step3();
	        	}
	        	step2(pd);//po = pd TODO:eh isso memo?
	        	
	        }
        }
    	
    }
    private void step3(){
    	List<Transition> ts = new ArrayList<Transition>();
    	
    	for(Transition t : As.getAllTransitions()){
    		//TODO: i foi alterado para i-1
    		if(t.getConditions().get(0).equals(sigma) && (new Integer(i-1)).equals(stateClassNumber(mapAs,t.getStateFrom())) && stateClassNumber(mapAs,t.getStateTo())==null){
    			ts.add(t);
    		}    		
    	}
    	if(!ts.isEmpty()){
			j++;
			step4(ts);    					
    	}
    }
    private void step5(){
    	State novo = Ac.createState("S"+j);
    	addToClassNumber(mapAc,novo,j);//attach j
    	if(mapAc.get(j-1).size()!=1)
    		System.out.println("Latet anguilla sub relva!");
    	else {
    		//((State)mapAc.get(j-1).toArray()[0]).createTransitionTo(novo, sigma,null);
    		State s = (State)mapAc.get(j-1).toArray()[0];
    		safeCreateTransition(s, novo, sigma);
    	}
    	
    	step6();    	
    }
    private void step4(List<Transition> ts){
    	for(Transition t:ts)
    		addToClassNumber(mapAs,t.getStateTo(),j);//attach j
    	
    	step5();
    }
    
    private void step6(){//find cycles
    	List<Transition> ts = new ArrayList<Transition>();
    	
    	for(Transition t : As.getAllTransitions()){
    		if(		stateClassNumber(mapAs,t.getStateTo()) != null &&
    				stateClassNumber(mapAs,t.getStateFrom())!=null &&
    				stateClassNumber(mapAs,t.getStateTo()) <= stateClassNumber(mapAs,t.getStateFrom()) &&
    				! t.isEpsilon()){
    			//TODO: <= mudado em relacao ao escrito no passo 6, de modo q o 7 fizesse sentido!
    			//TODO: condicao de anulidade inserta por nohs, derivada de observacoes empiricas
    			ts.add(t);
    		}    		
    	}
    	if(!ts.isEmpty()){			
			step7(ts);    		
    	}else{
    		step16();
    	}
    }
    private void step7(List<Transition> ts){
    	Set<String> setro = new HashSet<String>();
    	
    	for(Transition t:ts){
    		x = stateClassNumber(mapAs,t.getStateFrom());
    		y = stateClassNumber(mapAs,t.getStateTo());
    		rho = t.getConditions().get(0);
    		setro.add(rho);
    		
    		if(x == y)
    			step8();
    		else //pelas condicoes de chamada, necessariamente y<x
    			step11();
    	}
    	
    	//TODO: parte estranha do algoritmo da IVONE, mas q julgamos exencial: propagacao no Ap e no As
    	for (State s: As.getAllStates()) {
	    	if((new Integer(x)).equals(stateClassNumber(mapAs,s)))
	    		propaga(mapAs, s, setro);
    	}
    	for (State s: Ap.getAllStates()) {
	    	if((new Integer(x)).equals(stateClassNumber(mapAp,s)))
	    		propaga(mapAp, s, setro);
    	}
    }

    private boolean safeCreateTransition(State from, State to, String cond) {
        
        for (Transition t: from.getAllTransitions()) {
            if (t.getStateTo().equals(to) && t.getConditions().contains(cond))
                return false;
        }
        from.createTransitionTo(to, cond, null);
        return true;
    }
    
    private void propaga(Map<Integer, Set<State>> map, State s, Set<String> rhos) {
    
    	for(Transition t: s.getAllTransitions()) {
    		if (rhos.contains(t.getConditions().get(0)) && stateClassNumber(map,t.getStateTo())==null) {
    			addToClassNumber(map, t.getStateTo(), x);
    			propaga(map, t.getStateTo(), rhos);
    		}
    	}
    }
    
    private void step8(){
    	State sx = (State)mapAc.get(x).toArray()[0];
    	//sx.createTransitionTo(sx, rho, null);
    	safeCreateTransition(sx, sx, rho);
    	step9();
    }
    
    
    
    private void step9(){
    	List<Transition> ts = new ArrayList<Transition>();
    	
    	for(Transition t : Ap.getAllTransitions()){
    		if(		t.getConditions().contains(sigma) &&
    				(new Integer(x-1)).equals(stateClassNumber(mapAp,t.getStateFrom())) && 
    				stateClassNumber(mapAp,t.getStateTo()) == null){
    			//TODO: x alterado para x-1
    			//TODO: rho mudado para sigma
    			ts.add(t);
    		}    		
    	}
    	if(!ts.isEmpty()){			
			step10(ts);    		
    	}else{
    		step16();
    	}
    	
    	
    }
    private void step10(List<Transition> ts){
    	for(Transition t: ts) addToClassNumber(mapAp,t.getStateTo(),y);
    	step11();
    }
    private void step11(){ // TODO: falta na IVONE?
    	for(Transition t : As.getAllTransitions()){
    		if(		t.getConditions().contains(rho) &&
    				(new Integer(y)).equals(stateClassNumber(mapAs,t.getStateTo())) &&
    				stateClassNumber(mapAs,t.getStateFrom()) == null){
    			addToClassNumber(mapAs,t.getStateFrom(),x);
    		}    		
    	}
    	step12();
    }

    //step12: implementado no step7
    private void step12(){
    	if(x > y)
    		step13();
    	else
    		System.out.println("pela logica, n deveria ter chegado aqui");//step16();
    }
    private void step13(){
    	State sx = (State)mapAc.get(x).toArray()[0];
    	State sy = (State)mapAc.get(y).toArray()[0];
    	sx.createTransitionTo(sy, rho, null);
    	step14();    	
    }
    private void step14(){
    	List<Transition> ts = new ArrayList<Transition>();
    	
    	for(Transition t : Ap.getAllTransitions()){
    		if(t.getConditions().contains(rho) &&
    				(new Integer(x)).equals(stateClassNumber(mapAp,t.getStateFrom()))) {
    			ts.add(t);
    		}    		
    	}
    	if(!ts.isEmpty()){			
			step15(ts);    		
    	}else{
    		step16();
    	}
    	
    }

    private void step15(List<Transition> ts){
    	for(Transition t: ts) addToClassNumber(mapAp,t.getStateTo(),y);
    	step16();
    	
    }

    private void step16(){
    	
    }
    
    private void step17(){
        
    }
    
    
}
